package org.convallaria.system.biz.service.impl;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.system.biz.service.SecurityAuditService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * 安全审计服务实现
 * 
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SecurityAuditServiceImpl implements SecurityAuditService {

    private final RedisTemplate<String, String> redisTemplate;

    private static final String AUDIT_LOG_PREFIX = "security:audit:";
    private static final String AUDIT_INDEX_PREFIX = "security:index:";
    private static final int AUDIT_LOG_EXPIRE_DAYS = 90;

    @Override
    public void recordSecurityEvent(SecurityEvent event) {
        try {
            // 记录到Redis
            String auditKey = AUDIT_LOG_PREFIX + event.getEventId();
            Map<String, String> auditData = buildAuditData(event);
            redisTemplate.opsForHash().putAll(auditKey, auditData);
            
            // 设置过期时间
            redisTemplate.expire(auditKey, java.time.Duration.ofDays(AUDIT_LOG_EXPIRE_DAYS));

            // 建立索引
            buildAuditIndex(event);

            // 记录到日志文件
            logSecurityEvent(event);

        } catch (Exception e) {
            log.error("记录安全事件失败", e);
        }
    }

    @Override
    public void recordLoginEvent(Long userId, String username, String ip, boolean success, String reason) {
        SecurityEvent event = SecurityEvent.builder()
                .eventType(success ? SecurityEventType.LOGIN_SUCCESS.name() : SecurityEventType.LOGIN_FAILURE.name())
                .userId(userId)
                .username(username)
                .ip(ip)
                .operation("LOGIN")
                .resource("AUTHENTICATION")
                .result(success ? "SUCCESS" : "FAILURE")
                .reason(reason)
                .build();

        recordSecurityEvent(event);
    }

    @Override
    public void recordPermissionChangeEvent(Long operatorId, Long targetUserId, String operation, String details) {
        SecurityEvent event = SecurityEvent.builder()
                .eventType(SecurityEventType.PERMISSION_GRANT.name())
                .userId(operatorId)
                .operation(operation)
                .resource("PERMISSION")
                .details(details)
                .result("SUCCESS")
                .build();

        recordSecurityEvent(event);
    }

    @Override
    public void recordSensitiveOperationEvent(Long userId, String operation, String resource, String details) {
        SecurityEvent event = SecurityEvent.builder()
                .eventType(SecurityEventType.SENSITIVE_OPERATION.name())
                .userId(userId)
                .operation(operation)
                .resource(resource)
                .details(details)
                .result("SUCCESS")
                .build();

        recordSecurityEvent(event);
    }

    @Override
    public void recordPasswordChangeEvent(Long userId, Long operatorId, boolean success, String reason) {
        SecurityEvent event = SecurityEvent.builder()
                .eventType(SecurityEventType.PASSWORD_CHANGE.name())
                .userId(operatorId != null ? operatorId : userId)
                .operation("PASSWORD_CHANGE")
                .resource("USER_ACCOUNT")
                .result(success ? "SUCCESS" : "FAILURE")
                .reason(reason)
                .build();

        recordSecurityEvent(event);
    }

    /**
     * 构建审计数据
     */
    private Map<String, String> buildAuditData(SecurityEvent event) {
        Map<String, String> data = new HashMap<>();
        data.put("eventId", event.getEventId());
        data.put("eventType", event.getEventType());
        data.put("userId", event.getUserId() != null ? event.getUserId().toString() : "");
        data.put("username", event.getUsername() != null ? event.getUsername() : "");
        data.put("ip", event.getIp() != null ? event.getIp() : "");
        data.put("userAgent", event.getUserAgent() != null ? event.getUserAgent() : "");
        data.put("operation", event.getOperation() != null ? event.getOperation() : "");
        data.put("resource", event.getResource() != null ? event.getResource() : "");
        data.put("details", event.getDetails() != null ? event.getDetails() : "");
        data.put("result", event.getResult() != null ? event.getResult() : "");
        data.put("reason", event.getReason() != null ? event.getReason() : "");
        data.put("eventTime", event.getEventTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        data.put("tenantId", event.getTenantId() != null ? event.getTenantId() : "");
        return data;
    }

    /**
     * 建立审计索引
     */
    private void buildAuditIndex(SecurityEvent event) {
        try {
            String dateKey = event.getEventTime().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            
            // 按日期索引
            String dateIndexKey = AUDIT_INDEX_PREFIX + "date:" + dateKey;
            redisTemplate.opsForSet().add(dateIndexKey, event.getEventId());
            redisTemplate.expire(dateIndexKey, java.time.Duration.ofDays(AUDIT_LOG_EXPIRE_DAYS));

            // 按用户索引
            if (event.getUserId() != null) {
                String userIndexKey = AUDIT_INDEX_PREFIX + "user:" + event.getUserId();
                redisTemplate.opsForSet().add(userIndexKey, event.getEventId());
                redisTemplate.expire(userIndexKey, java.time.Duration.ofDays(AUDIT_LOG_EXPIRE_DAYS));
            }

            // 按事件类型索引
            String typeIndexKey = AUDIT_INDEX_PREFIX + "type:" + event.getEventType();
            redisTemplate.opsForSet().add(typeIndexKey, event.getEventId());
            redisTemplate.expire(typeIndexKey, java.time.Duration.ofDays(AUDIT_LOG_EXPIRE_DAYS));

            // 按IP索引
            if (event.getIp() != null) {
                String ipIndexKey = AUDIT_INDEX_PREFIX + "ip:" + event.getIp();
                redisTemplate.opsForSet().add(ipIndexKey, event.getEventId());
                redisTemplate.expire(ipIndexKey, java.time.Duration.ofDays(AUDIT_LOG_EXPIRE_DAYS));
            }

        } catch (Exception e) {
            log.error("建立审计索引失败", e);
        }
    }

    /**
     * 记录到日志文件
     */
    private void logSecurityEvent(SecurityEvent event) {
        StringBuilder logMessage = new StringBuilder();
        logMessage.append("SECURITY_AUDIT: ");
        logMessage.append("eventId=").append(event.getEventId()).append(", ");
        logMessage.append("eventType=").append(event.getEventType()).append(", ");
        logMessage.append("userId=").append(event.getUserId()).append(", ");
        logMessage.append("username=").append(event.getUsername()).append(", ");
        logMessage.append("ip=").append(event.getIp()).append(", ");
        logMessage.append("operation=").append(event.getOperation()).append(", ");
        logMessage.append("resource=").append(event.getResource()).append(", ");
        logMessage.append("result=").append(event.getResult()).append(", ");
        logMessage.append("reason=").append(event.getReason()).append(", ");
        logMessage.append("eventTime=").append(event.getEventTime());

        if (SecurityEventType.LOGIN_FAILURE.name().equals(event.getEventType()) ||
            SecurityEventType.SECURITY_VIOLATION.name().equals(event.getEventType())) {
            log.warn(logMessage.toString());
        } else {
            log.info(logMessage.toString());
        }
    }
}
